﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Web;
using Vit.Common;
using Vit.Common.Extensions;
using Vit.Web;
using Vit.Web.Models;
using Vit.Mvc.Models;
using Vit.Web.Providers;

namespace Vit.Mvc.Providers
{
    public class XmlNavigationProvider : INavigationProvider
    {
        public const string DefaultNavigationXmlFile = "";

        public XmlNavigationProvider()
            : this(DefaultNavigationXmlFile)
        {
        }

        private XDocument doc = null;

        public XmlNavigationProvider(string configPath)
        {
            LoadConfiguration(configPath);
        }

        private void LoadConfiguration(string filePath)
        {
            if (filePath.StartsWith("~/"))
                filePath = HttpContext.Current.Server.MapPath(filePath);
            doc = XDocument.Load(filePath);
        }

        private NavigationItem ParseFromXElementWithChildren(XElement ele)
        {
            Stack<Tuple<XElement, NavigationItem>> stEle = new Stack<Tuple<XElement, NavigationItem>>();
            stEle.Push(new Tuple<XElement, NavigationItem>(ele, null));
            NavigationItem item = null;
            while (stEle.Count > 0)
            {
                var parent = stEle.Pop();
                var child = ParseFromXElement(parent.Item1, parent.Item2);
                if (item == null)
                    item = child;
                if (ele.HasElements)
                {
                    foreach (XElement ce in parent.Item1.Elements("nav"))
                    {
                        stEle.Push(new Tuple<XElement, NavigationItem>(ce, child));
                    }
                }
            }
            return item;
        }

        private NavigationItem ParseFromXElement(XElement ele, NavigationItem parent)
        {
            if (ele == null)
                return null;
            NavigationItem item = new NavigationItem()
            {
                Name = ele.AttributeValue("name", ""),
                Caption = ele.AttributeValue("caption", Name),
                Url = ele.AttributeValue("url", "")
            };

            if (parent != null)
            {
                item.Parent = parent;
                parent.Children.Add(item);
            }

            return item;
        }

        #region INavigationProvider Members

        public IList<INavigationItem> GetNavigationsByGroup(string groupName)
        {
            string key = string.Format("NavigationGroup:{0}", groupName);
            IList<INavigationItem> itemList = null;
            WebUnity.Current.CacheService.Run(p =>
            {
                if (p.Name.Equals(CommonCategories.CacheCategories.SiteCache))
                {
                    if (p.Contains(key))
                    {
                        itemList = p.GetData(key) as IList<INavigationItem>;
                        return true;
                    }
                }
                return false;
            });


            XElement group = doc.Root.XPathSelectElement(string.Format("group[translate(@name,'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')='{0}']", groupName.ToLower()));
            itemList = new List<INavigationItem>();
            foreach (var ele in group.Elements("nav"))
            {
                itemList.Add(ParseFromXElementWithChildren(ele));
            }
#if !DEBUG
            WebUnity.Current.CacheService.Run(p =>
                {
                    if (p.Name.Equals(CommonCategories.CacheCategories.SiteCache))
                    {
                        if (p.Contains(key) == false)
                            p.Add(key, itemList);
                        return true;
                    }
                    return false;
                });
#endif

            return itemList;
        }

        public INavigationItem GetNavigationItemByUrl(string group, string url)
        {
            var niGroup = GetNavigationsByGroup(group);
            Stack<INavigationItem> st = new Stack<INavigationItem>();
            foreach (var ni in niGroup)
                st.Push(ni);

            while (st.Count > 0)
            {
                var ni = st.Pop();
                if (IsNavigationItem(ni, url))
                {
                    return ni;
                }
                if (ni.Children != null && ni.Children.Count > 0)
                {
                    foreach (var sni in ni.Children)
                        st.Push(sni);
                }
            }

            return null;
        }

        public bool IsNavigationItem(INavigationItem ni, string url)
        {
            if (ni == null || string.IsNullOrEmpty(url))
                return false;
            return url.StartsWith(ni.Url, StringComparison.OrdinalIgnoreCase);
        }

        #endregion

        #region IProvider Members

        public string Name
        {
            get { return "XmlNavigationProvider"; }
        }

        #endregion
    }
}
